#include "OptimalAlgo.h"
#include <iostream>
#include <fstream>

OptimalAlgo::OptimalAlgo(SystemView* sys, int sortType, Writer* w, Parser* prs) : Algorithm(sys,sortType,w, prs)
{	
	sortedExtents = new vector<ExtentStatsAlgo*>();
}

OptimalAlgo::~OptimalAlgo(void)
{
	delete ssdExtents;
	delete fcExtents;
	delete sataExtents;
	delete sortedExtents;
}

void OptimalAlgo::createSortedExtents(vector<ExtentEntry*>* extents){
	vector<ExtentEntry*>::iterator itExs;
	for(itExs = extents->begin(); itExs != extents->end(); itExs++){
		ExtentStatsAlgo* ex = new ExtentStatsAlgo((*itExs)->getMy_extent());
		ex->addStat((*itExs)->getMy_stat());
		ex->calculateTotalRM();
		sortedExtents->push_back(ex);
		delete (*itExs);
	}
	delete extents;
}

void OptimalAlgo::runAlgo(int iteration){

	all_damages->clear();

	double ssdSpace = sys->getAllocation()->getSSD_size() / sys->getAllocation()->getExtent_size(); //num of extents that can be in SSD according to it's space- 300GB (Extent = 10MB)
	double fcSpace = sys->getAllocation()->getFC_size() / sys->getAllocation()->getExtent_size();
	double sataSpace = sys->getAllocation()->getSATA_size() / sys->getAllocation()->getExtent_size();
	

	vector<ExtentEntry*>::iterator extentsIt;
	vector<ExtentEntry*>* extents;
	vector<ExtentStatsAlgo*>* temp;
	while ((extents = parser->ParseNextTimeSlice()) != 0){
		int time = extents->front()->getMy_time();
		createSortedExtents(extents);

		temp = sorter->sort(sortedExtents); //sort sorted extents for current time slice
		delete sortedExtents;
		sortedExtents = temp;
		
		vector<ExtentStatsAlgo*>::iterator sortIt;
		for(sortIt = sortedExtents->begin(); sortIt != sortedExtents->end(); sortIt++){	
			if(ssdExtents->size() <= ssdSpace && ssd->checkUtilization(time,(*sortIt))){
				ssd->encUtilization(time, *sortIt);
				ssdExtents->push_back(*sortIt);
			}
			else if((fcExtents->size() <= fcSpace) && fc->checkUtilization(time,*sortIt)){
				fc->encUtilization(time, *sortIt);
				fcExtents->push_back(*sortIt);
			}
			else {
				if (DEBUG>0){
					if (sataExtents->size() > sataSpace)
						writer->writeLog("SATA space is over!!!!!");
					if (!sata->checkUtilization(time,*sortIt) )
						writer->writeLog("SATA util not pass!!!");
				}

				sata->encUtilization(time, *sortIt);
				sataExtents->push_back(*sortIt);
			}
		}

		// calling function to calculate each lun's response time for this time slice
		timeSliceCalc->responseTime(ssdExtents, fcExtents, sataExtents, time, ssd, fc, sata, 0, 0, 0, 0);
		(*all_damages)[time] = timeSliceCalc->sum_damage(time);

		
		/*******************************************************New function call to write extent placements every time slice************************************************************/
		
		if (iteration==1){
			WritePlacementsNoPriority(ssdExtents, fcExtents, sataExtents, time);
		}
		
		if (iteration==3){
			WritePlacements(ssdExtents, fcExtents, sataExtents, time);
		}

		/********************************************************************************************************************************************************************************/


		//calc Division of groups percents	
		ssdDivisionPercGroup1->insert(pair<int,double>(time,divisionOfPriorityGroup(ssdExtents,1,time)));
		fcDivisionPercGroup1->insert(pair<int,double>(time,divisionOfPriorityGroup(fcExtents,1,time)));
		sataDivisionPercGroup1->insert(pair<int,double>(time,divisionOfPriorityGroup(sataExtents,1,time)));

		ssdDivisionPercGroup2->insert(pair<int,double>(time,divisionOfPriorityGroup(ssdExtents,2,time)));
		fcDivisionPercGroup2->insert(pair<int,double>(time,divisionOfPriorityGroup(fcExtents,2,time)));
		sataDivisionPercGroup2->insert(pair<int,double>(time,divisionOfPriorityGroup(sataExtents,2,time)));

		ssdDivisionPercGroup3->insert(pair<int,double>(time,divisionOfPriorityGroup(ssdExtents,3,time)));
		fcDivisionPercGroup3->insert(pair<int,double>(time,divisionOfPriorityGroup(fcExtents,3,time)));
		sataDivisionPercGroup3->insert(pair<int,double>(time,divisionOfPriorityGroup(sataExtents,3,time)));

		//delete all
		vector<ExtentStatsAlgo*>::iterator iter;
		for (iter=sortedExtents->begin(); iter!=sortedExtents->end(); iter++){
			(*iter)->deleteContent();
			delete (*iter);
		}
		
		sortedExtents->clear();
		fcExtents->clear();
		ssdExtents->clear();
		sataExtents->clear();
		ssd->clearUtilData();
		fc->clearUtilData();
		sata->clearUtilData();

		cout << "time: " << time << " damage: " << all_damages->at(time) << endl;
	}

	//******************** writing damage to files for graphs **************************************//
	string algoName="Optimal";
	string methodName="";
	switch(iteration){
		case 1:
			methodName="Priority=1";
			break;
		case 2:
			methodName="Priority=1RT";
			break;
		case 3:
			methodName="gradientDescent";
			break;
		case 4:
			methodName="lastGD";
			break;
	}
	writer->writeAlgoDamage(iteration, all_damages, algoName, methodName);
	writer->writeMethodDamage(iteration, all_damages, algoName, methodName);
	writer->writeDivisionOfPriorityGroups(iteration,1,ssdDivisionPercGroup1,fcDivisionPercGroup1,sataDivisionPercGroup1);
	writer->writeDivisionOfPriorityGroups(iteration,2,ssdDivisionPercGroup2,fcDivisionPercGroup2,sataDivisionPercGroup2);
	writer->writeDivisionOfPriorityGroups(iteration,3,ssdDivisionPercGroup3,fcDivisionPercGroup3,sataDivisionPercGroup3);
	//************************************************************************************************//

}

double OptimalAlgo::divisionOfPriorityGroup(vector<ExtentStatsAlgo*>* l, int groupNo, int time){
	int num = 0;
	vector<ExtentStatsAlgo*>::iterator itl;
	for (itl = l->begin(); itl != l->end(); itl++){
		ExtentStatsAlgo* ex = *itl;
		if (ex->getExtent()->getLun()->getPriorityGroup() == groupNo && (ex->getStat(time)!=0) && (ex->getStat(time)->getStat(rand_rm)>0))
			num++;
	}
	if (l->size() == 0)
		return 0;
	else
		return ((double)num)/((double)l->size());
}